package com.soup.fs.impl;

import com.soup.commons.IdentifierGenerator;
import com.soup.commons.impl.CustomIdentifierGenerator;
import com.soup.fs.FileSystem;
import com.soup.fs.bean.Node;
import com.soup.fs.bean.Storage;
import com.soup.fs.dao.FileDao;

import java.io.*;
import java.sql.SQLException;
import java.util.Comparator;
import java.util.List;
import java.util.Random;

public class CustomFileSystem implements FileSystem {

    private static CustomFileSystem customFileSystem = null;

    private FileDao fileDao = null;

    private static final int MAX = 20;
    private static final int DIV = 1024 * 1024 * 512;
    private static final Random random = new Random();
    private static final IdentifierGenerator identifierGenerator = new CustomIdentifierGenerator();

    private static final File base = new File("soup-fs/storage");

    static {
        if (!base.exists()) {
            boolean mkdir = base.mkdir();
            if (mkdir) {
                for (int i = 0; i < MAX; i++) {
                    File ns = new File(base,i+"");
                    boolean mk = ns.mkdir();
                    if (!mk) {
                        break;
                    }
                }
            }
        }
    }

    private CustomFileSystem(){
        fileDao = new FileDao();
    }

    public static CustomFileSystem getCustomFileSystem() {
        synchronized (CustomFileSystem.class) {
            if (customFileSystem == null) {
                customFileSystem = new CustomFileSystem();
                return customFileSystem;
            }
        }
        return customFileSystem;
    }

    public Storage getStorage(String fileId) {
        try {
            return fileDao.query(fileId);
        } catch (SQLException throwable) {
            throwable.printStackTrace();
        }
        return null;
    }

    public byte[] download(String fileId) throws Exception {

        Storage query = fileDao.query(fileId);
        List<Node> nodes = query.getNodes();
        nodes.sort(Comparator.comparingInt(o -> Integer.parseInt(o.getNodeId())));
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

        nodes.forEach(node -> {
            try {
                FileInputStream fileInputStream = new FileInputStream(new File(base,node.getPath()));

                int len = 0;
                byte[] bytes = new byte[1024];
                while ( (len = fileInputStream.read(bytes)) != -1 ) {
                    outputStream.write(bytes,0,len);
                }
                fileInputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        });

        return outputStream.toByteArray();
    }

    public String getName(String fileId) {
        try {
            return fileDao.query(fileId).getName();
        } catch (SQLException throwable) {
            throwable.printStackTrace();
        }
        return null;
    }

    public String getMeta(String fileId) {
        try {
            return fileDao.query(fileId).getMeta();
        } catch (SQLException throwable) {
            throwable.printStackTrace();
        }
        return null;
    }

    public String getSuffix(String fileId) {
        try {
            return fileDao.query(fileId).getSuffix();
        } catch (SQLException throwable) {
            throwable.printStackTrace();
        }
        return null;
    }

    public String upload(InputStream inputStream, String name, String suffix, String meta) throws Exception{

        Storage storage = new Storage();
        List<Node> nodes = storage.getNodes();

        storage.setFileId(identifierGenerator.nextId(null)+"");
        storage.setName(name);
        storage.setSuffix(suffix);
        storage.setMeta(meta);

        int index = 0;
        int length = inputStream.available();
        byte[] bytes = new byte[DIV];
        while (length - DIV > 0) {
            String path = Writer(inputStream, DIV, bytes);
            nodes.add(new Node(index + "",path));
            length = length - DIV;
            index ++;
        }
        byte[] surplus = new byte[length];
        String path = Writer(inputStream, length, surplus);
        nodes.add(new Node(index + "",path));

        fileDao.insert(storage);
        return storage.getFileId();
    }

    private String Writer(InputStream inputStream, int length, byte[] surplus) throws IOException {
        String child = random.nextInt(MAX) + "/" + identifierGenerator.nextId(null);
        FileOutputStream outputStream = new FileOutputStream(new File(base,child));
        int read = inputStream.read(surplus);
        if (read == length) {
            outputStream.write(surplus);
        }
        outputStream.close();
        return child;
    }

    public String upload(InputStream inputStream, String originalName) throws Exception{
        int point = originalName.lastIndexOf('.');
        String name = originalName.substring(0,point);
        String suffix = originalName.substring(point + 1);
        return upload(inputStream,name,suffix,null);
    }

}
